/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_EdgeGeometry3_h__
#define __dylab_EdgeGeometry3_h__

//===========================================================================================
#include <dylab/utils/geometry/Ray3.h>
#include <dylab/utils/exceptions/ENumericError.h>
#include <dylab/utils/exceptions/EInvalidArgument.h>

//===========================================================================================
namespace dylab {

    /** 
     */
    class DYLAB_EXPORT EdgeGeometry3 
    {
    public:
        ///
        enum vertexidx_t
        {
            V_0 = 0,
            V_1
        };
        ///
        class DYLAB_EXPORT VertexIdxIterator : public EnumIterator<vertexidx_t, V_0, V_1> { };
        ///
        enum { V_COUNT = V_1 + 1 };
        ///
        typedef std::pair<Math::pointcls_t, vertexidx_t> pointcls_t;
    protected:       
        /// two edge vertices
        Vector3 mVertices[V_COUNT];
    public:

        /**          
         */
        EdgeGeometry3() 
                { }

        /**
        */
        EdgeGeometry3(const Vector3 & _v0, const Vector3 & _v1) 
                { mVertices[V_0] = _v0, mVertices[V_1] = _v1; }

        /**
        */
        EdgeGeometry3(const Vector3 (&_vertices)[V_COUNT])
                { for (VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++) mVertices[vertexIdx] = _vertices[vertexIdx]; }

        /**
        */
        EdgeGeometry3(const EdgeGeometry3 & _edge) 
                { for (VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++) mVertices[vertexIdx] = _edge.mVertices[vertexIdx]; }

        /** 
         */
        Vector3 & vertex(vertexidx_t _vertexIdx) 
                { return mVertices[_vertexIdx]; }

        /** 
         */
        const Vector3 & vertex(vertexidx_t _vertexIdx) const
                { return mVertices[_vertexIdx]; }

        /**
        */
        Vector3 & operator[] (vertexidx_t _vertexIdx) 
                { return mVertices[_vertexIdx]; }
        
        /**
        */
        const Vector3 & operator[] (vertexidx_t _vertexIdx) const
                { return mVertices[_vertexIdx]; }

        /**
        */
        Vector3 & operator[] (uint32_t _index) 
                { 
                    DYLAB_ASSERT(_index < V_COUNT);
                    return mVertices[_index]; 
                }
        
        /**
        */
        const Vector3 & operator[] (uint32_t _index) const
                { 
                    DYLAB_ASSERT(_index < V_COUNT);
                    return mVertices[_index]; 
                }

        /** 
         */
        Vector3 getDirectionV0V1() const
                { return mVertices[V_1] - mVertices[V_0]; }

        /** 
         */
        Vector3 getDirectionV1V0() const
                { return mVertices[V_0] - mVertices[V_1]; }

        /** 
         */
        Ray3 getRayFromV0() const
                { return Ray3(mVertices[V_0], getDirectionV0V1()); }

        /** 
         */
        Ray3 getRayFromV1() const
                { return Ray3(mVertices[V_1], getDirectionV1V0()); }

        /** 
         */
        bool isParallelTo(const EdgeGeometry3 & _edge) const
                { return getDirectionV0V1().cross(_edge.getDirectionV0V1()).almostZero(); }

        /** 
         */
        bool isDegenerated() const
                { return mVertices[V_0].almostEqual(mVertices[V_1]); }

        /** 
         */
        Vector3 computeCenter() const
                { return (mVertices[V_0] + mVertices[V_1]) / DYLAB_R(2); }

        /** 
         */
        real_t computeLength() const
                { return getDirectionV0V1().length(); }

        /**
        */
        bool computeParameter(const Vector3 & _p, real_t & _t, 
			real_t _paramTolerance = Math::EQUALITY_TOLERANCE) const;

        /**
        */
        pointcls_t classifyPoint(const Vector3 & _p, real_t _paramTolerance = Math::EQUALITY_TOLERANCE, 
			real_t _borderTolerance = Math::EQUALITY_TOLERANCE) const;

        /**
        */
        operator String() const                
				{ return String::fmt("[%1% -> %2%]", mVertices[V_0], mVertices[V_1]); }
    };

    //===========================================================================================    
    inline std::ostream & operator << (std::ostream & _os, const EdgeGeometry3 & _o)
            { return _os << String(_o); }

    //===========================================================================================    
    DYLAB_EXPORT std::ostream & operator << (std::ostream & _os, const EdgeGeometry3::pointcls_t & _pointCls);
}
//===========================================================================================
#endif // __dylab_EdgeGeometry3_h__
